// phalanx builtin_vector_iterator.hpp.cu header file

// Copyright (c) 2011 - 2011 Kohei Takahashi (Flast).
// Distributed under the MIT license. for more detail see COPYING.

#ifndef IG_PHALANX_ITERATOR_BUILTIN_VECTOR_ITERATOR_HPP_CU_ONCE_
#define IG_PHALANX_ITERATOR_BUILTIN_VECTOR_ITERATOR_HPP_CU_ONCE_

#include <phalanx/config.hpp.cu>

#include <iterator>

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

#include <phalanx/detail/retypedef.hpp.cu>

#include <phalanx/iterator/detail/container_traits.hpp.cu>

#include <phalanx/type_traits/builtin_traits.hpp.cu>
#include <phalanx/type_traits/common_type.hpp.cu>
#include <phalanx/type_traits/is_same.hpp.cu>
#include <phalanx/type_traits/is_builtin_vector.hpp.cu>
#include <phalanx/type_traits/remove_const.hpp.cu>

#include <phalanx/utility/pair.hpp.cu>
#include <phalanx/utility/swap.hpp.cu>
#include <phalanx/utility/enable_if.hpp.cu>

namespace phalanx
{

template < typename >
struct builtin_vector_iterator;

namespace iterator_detail
{

template < typename _Container,
  ::std::size_t = vector_type_traits<
    typename remove_const< _Container >::type >::size_value >
struct _iterator_deref_helper;

template < typename _Container >
struct _iterator_deref_helper< _Container, 0 >
{
    typedef builtin_vector_iterator< _Container > _impl_type;

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    static typename container_value< _Container >::type &
    _to_be_undefined_behavior( void )
    {
        typedef typename container_value< _Container >::type value_type;
        value_type _ = value_type();
        // XXX: GCC warning returning reference to local variable.
        value_type *_p = &_;
        return *_p;
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    static typename container_reference< _Container >::type
    _deref( const _impl_type &_itr )
    { return _to_be_undefined_behavior(); }
};

#define _ITERATOR_DEREF_HELPER_IMPL( _unused_z, _num, _data ) \
  template < typename _Container > \
  struct _iterator_deref_helper< _Container, BOOST_PP_INC( _num ) > \
  { \
      typedef _iterator_deref_helper< _Container, _num > base_t; \
      PHALANX_PP_RETYPEDEF( base_t, _impl_type ); \
      \
      PHALANX_CONFIG_DEFAULT_FUNCTION( true ) \
      static typename container_reference< _Container >::type \
      _deref( const _impl_type &_itr ) \
      { \
          typedef typename container_difference< _Container >::type difference_type; \
          if ( _itr.base().second == difference_type( _num ) ) \
          { \
              return _itr.base().first-> \
                BOOST_PP_TUPLE_ELEM( \
                  BOOST_PP_TUPLE_ELEM( 2, 0, _data ), \
                  _num, \
                  BOOST_PP_TUPLE_ELEM( 2, 1, _data ) ); \
          } \
          return base_t::_deref( _itr ); \
      } \
  };

BOOST_PP_REPEAT( 4, _ITERATOR_DEREF_HELPER_IMPL, ( 4, ( x, y, z, w ) ) );

#undef _ITERATOR_DEREF_HELPER_IMPL

template < typename container_type >
struct _container_iterator
  : public ::std::iterator<
      ::std::random_access_iterator_tag,
      typename container_value< container_type >::type,
      typename container_difference< container_type >::type,
      typename container_pointer< container_type >::type,
      typename container_reference< container_type >::type > {};

template < typename _subclass, typename _Container,
  bool = is_builtin_vector< typename remove_const< _Container >::type >::value >
struct _builtin_vector_iterator_impl
  : public _container_iterator< _Container >
{
    typedef _container_iterator< _Container > _base_t;

    typedef _subclass  _this_type;
    typedef _Container container_type;

    PHALANX_PP_RETYPEDEF( _base_t, difference_type );
    PHALANX_PP_RETYPEDEF( _base_t, reference );

protected:
    container_type  *container;
    difference_type current;

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    void
    swap( _builtin_vector_iterator_impl &_x )
    {
        using ::phalanx::swap;
        swap( container, _x.container );
        swap( current, _x.current );
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type &
    _instance( void )
    { return static_cast< _this_type & >( *this ); }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    const _this_type &
    _instance( void ) const
    { return static_cast< const _this_type & >( *this ); }

public:
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _builtin_vector_iterator_impl( container_type *_x, difference_type _n )
      : container( _x ), current( _n ) {}

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    pair< container_type *, difference_type >
    base( void ) const
    { return make_pair( container, current ); }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    reference
    operator*( void ) const
    { return _iterator_deref_helper< container_type >::_deref( _instance() ); }

    template < typename T >
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type &
    operator=( const builtin_vector_iterator< T > &_x )
    {
        _this_type _tmp( _x );
        _tmp.swap( *this );
        return _instance();
    }

    // XXX: operator-> is not provided for builtin vector type iterator.

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type &
    operator++( void )
    {
        ++current;
        return _instance();
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type
    operator++( int )
    {
        _this_type _tmp = _instance();
        ++current;
        return _instance();
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type &
    operator--( void )
    {
        --current;
        return _instance();
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type
    operator--( int )
    {
        _this_type _tmp = _instance();
        --current;
        return _instance();
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type
    operator+( difference_type _n ) const
    { return _this_type( *container, current + _n ); }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type &
    operator+=( difference_type _n )
    {
        current += _n;
        return _instance();
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type
    operator-( difference_type _n ) const
    { return _this_type( *container, current - _n ); }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _this_type &
    operator-=( difference_type _n )
    {
        current -= _n;
        return _instance();
    }

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    reference
    operator[]( difference_type _n ) const
    {
        _this_type _tmp = _instance();
        _tmp += _n;
        return *_tmp;
    }
};

template < typename _subclass, typename _Container >
struct _builtin_vector_iterator_impl< _subclass, _Container, false >
  : public _container_iterator< _Container >
{
    typedef _container_iterator< _Container > _base_t;

    typedef _subclass  _this_type;
    typedef _Container container_type;

    PHALANX_PP_RETYPEDEF( _base_t, difference_type );

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    _builtin_vector_iterator_impl( container_type *, difference_type ) {}
};

} // namespace iterator_detail

template < typename _Container >
struct builtin_vector_iterator
  : public iterator_detail::_builtin_vector_iterator_impl<
      builtin_vector_iterator< _Container >, _Container >
{
    typedef iterator_detail::_builtin_vector_iterator_impl<
      builtin_vector_iterator< _Container >, _Container > _base_t;
    PHALANX_PP_RETYPEDEF( _base_t, container_type );
    PHALANX_PP_RETYPEDEF( _base_t, difference_type );

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    builtin_vector_iterator( void )
      : _base_t( 0, static_cast< difference_type >( -1 ) ) {}

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    explicit
    builtin_vector_iterator( container_type &_x,
      difference_type _n = static_cast< difference_type >( -1 ) )
      : _base_t( &_x, _n ) {}

    template < typename T >
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    builtin_vector_iterator( const builtin_vector_iterator< T > &_x )
      : _base_t( _x.container, _x.current ) {}
};

// FIXME: Those operators should use base().

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator==( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{ return _x.base() == _y.base(); }

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator!=( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{ return !( _x == _y ); }

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator<( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{
    return _x.base().first == _y.base().first
        && _x.base().second < _y.base().second;
}

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator>( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{ return _y < _x; }

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator<=( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{ return !( _x > _y ); }

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator>=( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{ return !( _x < _y ); }

template < typename _Container_l, typename _Container_r >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
typename lazy_enable_if<
  is_same<
    typename remove_const< _Container_l >::type,
    typename remove_const< _Container_r >::type >,
  common_type<
    typename container_difference< _Container_l >::type,
    typename container_difference< _Container_r >::type > >::type
operator-( const builtin_vector_iterator< _Container_l > &_x,
  const builtin_vector_iterator< _Container_r > &_y )
{
    return ( _x.base().first + _x.base().second )
        - ( _y.base().first + _y.base().second );
}

} // namespace phalanx

#endif // IG_PHALANX_ITERATOR_BUILTIN_VECTOR_ITERATOR_HPP_CU_ONCE_
